export const encodeStringToUnit8Array = (str: string): Uint8Array => {
  const buffers = parseUinicodes(str);
  const uft8Unicode = parseUtf8Codes(buffers);
  return Uint8Array.from(uft8Unicode);
};

export const decodeUnit8ArrayToString = (buffer: Uint8Array) => {
  const unicodes = getUnicodeWidthUtf8(buffer);
  const str = getStringFromUnicode(unicodes);
  return str;
};

const parseUinicodes = (str: string): Array<number> => {
  const unicodeList = [];
  /** str 字符串字节数 */
  for (let i = 0; i < str.length; i++) {
    /** 获取到 当前为 utf16 编码的unicode */
    let ch1 = str.charCodeAt(i);
    /** 当ch1 小于 D800 那么确定为基础平面 */
    if (ch1 < 0xd800) {
      unicodeList.push(ch1);
    } else {
      let ch2 = str.charCodeAt(i + 1);
      let unicode = isSMP(ch1, ch2);
      if (unicode) {
        i += 1;
        unicodeList.push(unicode);
      } else {
        unicodeList.push(ch1);
      }
    }
  }
  return unicodeList;
};

const isSMP = (chartCode1: number, chartCode2: number) => {
  // & FC00 取高八位 判断 c1 和c2 是否属于 H 和 L
  if ((chartCode1 & 0xfc00) === 0xd800 && (chartCode2 & 0xfc00) === 0xdc00) {
    return (((chartCode1 & 0x3ff) << 10) | (chartCode2 & 0x3ff)) + 0x10000;
  }
  return undefined;
};

const parseUtf8Codes = (unicodes: Array<number>): Array<number> => {
  const buffer: Array<number> = [];
  for (let k in unicodes) {
    const unicode = unicodes[k];
    if (unicode <= 0x7f) {
      buffer.push(unicode);
    } else if (unicode <= 0xff) {
      buffer.push((unicode >> 6) | 0xc0);
      buffer.push((unicode & 0x3c) | 0x80);
    } else if (unicode <= 0xffff) {
      buffer.push((unicode >> 12) | 0xe0);
      buffer.push(((unicode >> 6) & 0x3f) | 0x80);
      buffer.push((unicode & 0x3f) | 0x80);
    } else if (unicode <= 0x1fffff) {
      buffer.push((unicode >> 18) | 0xf0);
      buffer.push(((unicode >> 12) & 0x3f) | 0x80);
      buffer.push(((unicode >> 6) & 0x3f) | 0x80);
      buffer.push((unicode & 0x3f) | 0x80);
    }
  }
  return buffer;
};
/**
 * 从unit8Arry 中解析出unicode
 */
const getUnicodeWidthUtf8 = (array: Uint8Array) => {
  const unicodes: Array<number> = [];
  array.forEach((utf8Char, index) => {
    let n = 7;
    while (((utf8Char >> n) & 1) == 1) n--;
    n = 7 - n;
    if (n == 0) {
      unicodes.push(utf8Char);
    } else if (n > 1) {
      const utf8List = array.slice(index, index + n);
      const code = decodeUt8Unicode(utf8List);
      code && unicodes.push(code);
    }
  });
  return unicodes;
};

/**
 * @description  将utf8编码的二进制 转换为 unicode 16进制的码点
 * @param {array} codes
 * @returns
 */
const decodeUt8Unicode = (codes: ArrayLike<number>): number | null => {
  const len = codes.length;
  if (len == 0) return null;
  let start = codes[0];
  let result = start & (2 ** (7 - len) - 1);
  for (let i = 1; i < len; i++) {
    let follow = codes[i];
    if ((follow & 0x80) == 0x80) {
      result = (result << 6) | (follow & 0x3f);
    }
  }
  return result;
};

// 解析unicode
const getStringFromUnicode = (unicodes: Array<number>): string => {
  let str = "";
  unicodes.forEach((c) => {
    if (c < 0x10000) {
      str += String.fromCharCode(c);
    } else {
      let minus = c - 0x10000;
      let ch1 = (minus >> 10) | 0xd800;
      let ch2 = (minus & 0x3ff) | 0xdc00;
      str += String.fromCharCode(ch1, ch2);
    }
  });
  return str;
};
